/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the demonstration applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "QxTextButton.hpp"
#include "QxDemoItemAnimation.hpp"
#include "QxDemoTextItem.hpp"
#include "QxColors.hpp"
#include "QxMenuManager.hpp"

#define BUTTON_WIDTH 180
#define BUTTON_HEIGHT 19

class ButtonBackground : public QxDemoItem
{
public:

  QxTextButton::BUTTONTYPE type;

  bool highlighted;
  bool pressed;
  QSize logicalSize;

  ButtonBackground( QxTextButton::BUTTONTYPE type, bool highlighted, bool pressed, QSize logicalSize,
                    QGraphicsScene* scene, QGraphicsItem* parent) : QxDemoItem( scene, parent )
  {
    this->type        = type;
    this->highlighted = highlighted;
    this->pressed     = pressed;
    this->logicalSize = logicalSize;
    useSharedImage( QString(__FILE__) + static_cast<int>(type) + highlighted + pressed );
  }

protected:

  QImage* createImage( const QMatrix& matrix ) const {
    if( type == QxTextButton::SIDEBAR || type == QxTextButton::PANEL ) {
      return createRoundButtonBackground( matrix );
    } else {
      return createArrowBackground( matrix );
    }
  }

  QImage* createRoundButtonBackground( const QMatrix& matrix ) const {
    QRect scaledRect;
    scaledRect = matrix.mapRect( QRect(0, 0, this->logicalSize.width(), this->logicalSize.height()) );

    QImage* image = new QImage( scaledRect.width(), scaledRect.height(), QImage::Format_ARGB32_Premultiplied );
    image->fill( QColor(0, 0, 0, 0).rgba() );
    QPainter painter( image );
    painter.setRenderHint( QPainter::SmoothPixmapTransform );
    painter.setRenderHint( QPainter::Antialiasing );
    painter.setPen( Qt::NoPen );

    if( QxColors::useEightBitPalette ) {
      painter.setPen( QColor(120, 120, 120) );
      if( this->pressed ) {
        painter.setBrush( QColor(60, 60, 60) );
      } else if( this->highlighted ) {
        painter.setBrush( QColor(100, 100, 100) );
      } else {
        painter.setBrush( QColor(80, 80, 80) );
      }
    } else {
      QLinearGradient outlinebrush( 0, 0, 0, scaledRect.height() );
      QLinearGradient brush( 0, 0, 0, scaledRect.height() );

      brush.setSpread( QLinearGradient::PadSpread );
      QColor highlight(255, 255, 255, 70);
      QColor shadow(0, 0, 0, 70);
      QColor sunken(220, 220, 220, 30);
      QColor normal1(255, 255, 245, 60);
      QColor normal2(255, 255, 235, 10);

      if( this->type == QxTextButton::PANEL ) {
        normal1 = QColor(200, 170, 160, 50);
        normal2 = QColor(50, 10, 0, 50);
      }

      if( pressed ) {
        outlinebrush.setColorAt( 0.0f, shadow );
        outlinebrush.setColorAt( 1.0f, highlight );
        brush.setColorAt( 0.0f, sunken );
        painter.setPen( Qt::NoPen );
      } else {
        outlinebrush.setColorAt( 1.0f, shadow );
        outlinebrush.setColorAt( 0.0f, highlight );
        brush.setColorAt( 0.0f, normal1 );
        if( !this->highlighted ) {
          brush.setColorAt( 1.0f, normal2 );
        }
        painter.setPen( QPen( outlinebrush, 1 ) );
      }
      painter.setBrush( brush );
    }

    if( this->type == QxTextButton::PANEL ) {
      painter.drawRect( 0, 0, scaledRect.width(), scaledRect.height() );
    } else {
      painter.drawRoundedRect( 0, 0, scaledRect.width(), scaledRect.height(), 10, 90, Qt::RelativeSize );
    }
    return image;
  }

  QImage* createArrowBackground( const QMatrix& matrix ) const {
    QRect scaledRect;
    scaledRect = matrix.mapRect( QRect(0, 0, this->logicalSize.width(), this->logicalSize.height()) );

    QImage* image = new QImage( scaledRect.width(), scaledRect.height(), QImage::Format_ARGB32_Premultiplied );
    image->fill( QColor(0, 0, 0, 0).rgba() );
    QPainter painter( image );
    painter.setRenderHint( QPainter::SmoothPixmapTransform );
    painter.setRenderHint( QPainter::Antialiasing );
    painter.setPen( Qt::NoPen );

    if( QxColors::useEightBitPalette ) {
      painter.setPen( QColor(120, 120, 120) );
      if( this->pressed ) {
        painter.setBrush( QColor(60, 60, 60) );
      } else if( this->highlighted ) {
        painter.setBrush( QColor(100, 100, 100) );
      } else {
        painter.setBrush( QColor(80, 80, 80) );
      }
    } else {
      QLinearGradient outlinebrush( 0, 0, 0, scaledRect.height() );
      QLinearGradient brush( 0, 0, 0, scaledRect.height() );

      brush.setSpread( QLinearGradient::PadSpread );
      QColor highlight(255, 255, 255, 70);
      QColor shadow(0, 0, 0, 70);
      QColor sunken(220, 220, 220, 30);
      QColor normal1 = QColor(200, 170, 160, 50);
      QColor normal2 = QColor(50, 10, 0, 50);

      if( pressed ) {
        outlinebrush.setColorAt( 0.0f, shadow );
        outlinebrush.setColorAt( 1.0f, highlight );
        brush.setColorAt( 0.0f, sunken );
        painter.setPen( Qt::NoPen );
      } else {
        outlinebrush.setColorAt( 1.0f, shadow );
        outlinebrush.setColorAt( 0.0f, highlight );
        brush.setColorAt( 0.0f, normal1 );
        if( !this->highlighted ) {
          brush.setColorAt( 1.0f, normal2 );
        }
        painter.setPen( QPen(outlinebrush, 1) );
      }
      painter.setBrush( brush );
    }

    painter.drawRect( 0, 0, scaledRect.width(), scaledRect.height() );

    float xOff  = scaledRect.width() / 2;
    float yOff  = scaledRect.height() / 2;
    float sizex = 3.0f * matrix.m11();
    float sizey = 1.5f * matrix.m22();
    if( this->type == QxTextButton::UP ) {
      sizey *= -1;
    }
    QPainterPath path;
    path.moveTo( xOff, yOff + (5 * sizey) );
    path.lineTo( xOff - (4 * sizex), yOff - (3 * sizey) );
    path.lineTo( xOff + (4 * sizex), yOff - (3 * sizey) );
    path.lineTo( xOff, yOff + (5 * sizey) );
    painter.drawPath( path );

    return image;
  }
};

QxTextButton::QxTextButton( const QString& text, ALIGNMENT align, int userCode,
                            QGraphicsScene* scene, QGraphicsItem* parent, BUTTONTYPE type)
  : QxDemoItem( scene, parent )
{
  this->menuString  = text;
  this->buttonLabel = text;
  this->alignment   = align;
  this->buttonType  = type;
  this->userCode    = userCode;
  this->bgOn        = 0;
  this->bgOff       = 0;
  this->bgHighlight = 0;
  this->bgDisabled  = 0;
  this->state       = OFF;

  this->setAcceptsHoverEvents( true );
  this->setCursor( Qt::PointingHandCursor );

  // Calculate button size:
  const int w = 180;
  const int h = 19;
  if( type == SIDEBAR || type == PANEL ) {
    this->logicalSize = QSize(w, h);
  } else {
    this->logicalSize = QSize(int((w / 2.0f) - 5), int(h * 1.5f));
  }
}

void QxTextButton::setMenuString( const QString& menu )
{
  this->menuString = menu;
}

void QxTextButton::prepare()
{
  if( !this->prepared ) {
    this->prepared = true;
    this->setupHoverText();
    this->setupScanItem();
    this->setupButtonBg();
  }
}

QxTextButton::~QxTextButton()
{
  if( this->prepared ) {
    if( QxColors::useButtonBalls ) {
      delete this->scanAnim;
    }
  }
}

QRectF QxTextButton::boundingRect() const
{
  return QRectF(0, 0, this->logicalSize.width(), this->logicalSize.height());
};

void QxTextButton::setupHoverText()
{
  if( this->buttonLabel.isEmpty() ) {
    return;
  }

  QxDemoTextItem *textItem = new QxDemoTextItem( this->buttonLabel, QxColors::buttonFont(), QxColors::buttonText, -1, this->scene(), this );
  textItem->setZValue( zValue() + 2 );
  textItem->setPos( 16, 0 );
}

void QxTextButton::setupScanItem()
{
  if( QxColors::useButtonBalls ) {
    QxScanItem* scanItem = new QxScanItem( 0, this );
    scanItem->setZValue( zValue() + 1 );

    this->scanAnim = new QxDemoItemAnimation( scanItem );
    this->scanAnim->timeline->setLoopCount( 1 );

    float x = 1;
    float y = 1.5f;
    float stop = BUTTON_WIDTH - scanItem->boundingRect().width() - x;
    if( this->alignment == LEFT ) {
      this->scanAnim->setDuration( 2500 );
      this->scanAnim->setPosAt( 0.0, QPointF(x, y) );
      this->scanAnim->setPosAt( 0.5, QPointF(x, y) );
      this->scanAnim->setPosAt( 0.7, QPointF(stop, y) );
      this->scanAnim->setPosAt( 1.0, QPointF(x, y) );
      scanItem->setPos( QPointF(x, y) );
    } else {
      this->scanAnim->setPosAt( 0.0, QPointF(stop, y) );
      this->scanAnim->setPosAt( 0.5, QPointF(x, y) );
      this->scanAnim->setPosAt( 1.0, QPointF(stop, y) );
      scanItem->setPos( QPointF(stop, y) );
    }
  }
}

void QxTextButton::setState( STATE state )
{
  this->state = state;
  this->bgOn->setRecursiveVisible( state == ON );
  this->bgOff->setRecursiveVisible( state == OFF );
  this->bgHighlight->setRecursiveVisible( state == HIGHLIGHT );
  this->bgDisabled->setRecursiveVisible( state == DISABLED );
  this->setCursor( state == DISABLED ? Qt::ArrowCursor : Qt::PointingHandCursor );
}

void QxTextButton::setupButtonBg()
{
  this->bgOn        = new ButtonBackground( this->buttonType, true, true, this->logicalSize, this->scene(),   this );
  this->bgOff       = new ButtonBackground( this->buttonType, false, false, this->logicalSize, this->scene(), this );
  this->bgHighlight = new ButtonBackground( this->buttonType, true, false, this->logicalSize, this->scene(),  this );
  this->bgDisabled  = new ButtonBackground( this->buttonType, true, true, this->logicalSize, this->scene(),   this );
  this->setState( OFF );
}

void QxTextButton::hoverEnterEvent( QGraphicsSceneHoverEvent* )
{
  if( this->locked || this->state == DISABLED ) {
    return;
  }

  if( this->state == OFF ) {
    this->setState( HIGHLIGHT );

    if( QxColors::noAnimations && QxColors::useButtonBalls ) {
      // wait a bit in the beginning
      // to enhance the effect. Have to this here
      // so that the adaption can be dynamic
      this->scanAnim->setDuration( 1000 );
      this->scanAnim->setPosAt( 0.2, this->scanAnim->posAt( 0 ) );
    }

    if( QxMenuManager::instance()->window()->fpsMedian > 10
        || QxColors::noAdapt
        || QxColors::noTimerUpdate){
      if( QxColors::useButtonBalls ) {
        this->scanAnim->play( true, true );
      }
    }
  }
}

void QxTextButton::hoverLeaveEvent( QGraphicsSceneHoverEvent* event )
{
  Q_UNUSED( event );
  if( this->state == DISABLED ) {
    return;
  }

  this->setState( OFF );

  if( QxColors::noAnimations && QxColors::useButtonBalls ) {
    this->scanAnim->stop();
  }
}

void QxTextButton::mousePressEvent( QGraphicsSceneMouseEvent* )
{
  if( this->state == DISABLED ) {
    return;
  }

  if( this->state == HIGHLIGHT || this->state == OFF ) {
    this->setState( ON );
  }
}

void QxTextButton::mouseReleaseEvent( QGraphicsSceneMouseEvent* event )
{
  if( this->state == ON ) {
    this->setState( OFF );
    if( !this->locked && this->boundingRect().contains(event->pos()) ) {
      QxMenuManager::instance()->itemSelected( this->userCode, this->menuString );
    }
  }
}

void QxTextButton::animationStarted( int )
{
  if( this->state == DISABLED ) {
    return;
  }
  this->setState( OFF );
}
